/*
*
==============================================================================
*  Name        : 
*  Part of     : Omen Game Engine - DirectX 10 Plugin
*  Description : 
*  Version     : 1.0
*
*  Copyright (c) 2013 Lauri Kortevaara.
* 
==============================================================================
*/

#include "DirectX_10_Window.h"
#include <IOmenFactory.h>

#include <Windows.h>
#include <Strsafe.h>


using namespace Omen;

//=============================================================================
/**
**  Params
*/
HINSTANCE hInstance;

bool DirectX_10_Window::keys[256];
std::map<HWND,DirectX_10_Window*> DirectX_10_Window::g_mapHwndDX_10_Window;
WinMessageHandler DirectX_10_Window::g_ptrKernelMsgHandler = NULL; // Initialize the kernel message handler callback ptr

//=============================================================================
/**
** <summary> DirectX_10_Window constructor </summary>
**
*/
DirectX_10_Window::DirectX_10_Window()
    : m_hwnd(NULL)
{
}

//=============================================================================
/**
** <summary> DirectX_10_Window copy-constructor </summary>
**
*/
DirectX_10_Window::DirectX_10_Window( const DirectX_10_Window& other )
{
    m_hwnd = other.m_hwnd;
    m_windowHeight = other.m_windowHeight;
    m_windowWidth = other.m_windowWidth;
}


static void ShowLastError( LPTSTR lpszFunction )
{
    // Retrieve the system error message for the last-error code
    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError(); 

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    // Display the error message and exit the process

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
        (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
}
//=============================================================================
/**
** <summary> This Create a new DirectX_10_ Window </summary>
**
*/
void DirectX_10_Window::createWindow( const int& windowWidth, const int& windowHeight, const bool& bFullScreen )
{
    if( windowWidth <= 0 || windowHeight <= 0 )
        return;
    
    Omen::IKernel& kernel = GetOmenFactory().getKernel();

    std::wstringstream ss;

    // Store window width and window height to kernel as properties
    ss << windowWidth;
    std::wstring strWindowWidth = ss.str();
    kernel.setProperty( L"window_width", strWindowWidth );
    ss.str(L"");
    
    ss << windowHeight;
    std::wstring strWindowHeight = ss.str();
    kernel.setProperty( L"window_height", strWindowHeight );
    ss.str(L"");

    Omen::Float faspect = (Omen::Float) windowWidth / (Omen::Float)windowHeight;
    ss.precision(3);
    ss << faspect;
    std::wstring strAspect = ss.str();
    kernel.setProperty( L"window_aspect_ratio", strAspect );

    registerWindowClass();

    WNDCLASSEX wc;
    DEVMODE dmScreenSettings;
    int posX, posY;


    // Get an external pointer to this object.    
    //ApplicationHandle = this;

    // Get the instance of this application.
    //m_hinstance = GetModuleHandle(NULL);

    // Give the application a name.
    m_applicationName = L"Engine";

    // Setup the windows class with default settings.
    wc.style         = 0; //CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc   = (WNDPROC)DirectX_10_Window::WindowProcedure;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = m_hinstance;
    wc.hIcon         = LoadIcon(NULL, IDI_WINLOGO);
    wc.hIconSm       = wc.hIcon;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = m_applicationName;
    wc.cbSize        = sizeof(WNDCLASSEX);
    
    // Register the window class.
    RegisterClassEx(&wc);

    // Determine the resolution of the clients desktop screen.
    int screenWidth  = windowWidth!=0?windowWidth:GetSystemMetrics(SM_CXSCREEN);
    int screenHeight = windowHeight!=0?windowHeight:GetSystemMetrics(SM_CYSCREEN);

    DWORD   dwExStyle;                      // Window Extended Style
    DWORD   dwStyle;                        // Window Style
    
    // Setup the screen settings depending on whether it is running in full screen or in windowed mode.
    if(bFullScreen)
    {
        // If full screen set the screen to maximum size of the users desktop and 32bit.
        memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
        dmScreenSettings.dmSize       = sizeof(dmScreenSettings);
        dmScreenSettings.dmPelsWidth  = (unsigned long)windowWidth!=0?windowWidth:screenWidth;
        dmScreenSettings.dmPelsHeight = (unsigned long)windowHeight!=0?windowHeight:screenHeight;
        dmScreenSettings.dmBitsPerPel = 32;            
        dmScreenSettings.dmFields     = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

        // Change the display settings to full screen.
        ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);

        // Set the position of the window to the top left corner.
        posX = posY = 0;

        dwExStyle=WS_EX_APPWINDOW;                  // Window Extended Style
        dwStyle=WS_POPUP;                       // Windows Style
        ShowCursor(FALSE);                      // Hide Mouse Pointer
    }
    else
    {
        // If windowed then set it to 800x600 resolution.
        screenWidth  = windowWidth;
        screenHeight = windowHeight;

        // Place the window in the middle of the screen.
        posX = (GetSystemMetrics(SM_CXSCREEN) - screenWidth)  / 2;
        posY = (GetSystemMetrics(SM_CYSCREEN) - screenHeight) / 2;

        dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;     // Window Extended Style
        dwStyle   = WS_OVERLAPPEDWINDOW;                    // Windows Style
    }

    // Create the window with the screen settings and get the handle to it.
    m_hwnd = CreateWindowEx( dwExStyle, 
                            m_applicationName, 
                            m_applicationName, 
                            dwStyle,
                            posX, 
                            posY, 
                            screenWidth, 
                            screenHeight, 
                            NULL, 
                            NULL, 
                            m_hinstance, 
                            NULL);
    if( m_hwnd )
    {
        m_windowWidth = screenWidth;
        m_windowHeight = screenHeight;
    }
    else
    {
        m_windowWidth = 0;
        m_windowHeight = 0;
    }

    g_mapHwndDX_10_Window[m_hwnd] = this;

    // Bring the window up on the screen and set it as main focus.
    ShowWindow(m_hwnd, SW_SHOW);
    SetForegroundWindow(m_hwnd);
    SetFocus(m_hwnd);
}

//=============================================================================
/**
** <summary> Function will register a new window class for the DirectX_10_ Window purposes </summary>
**
*/
void DirectX_10_Window::registerWindowClass()
{
    WNDCLASSEX ex;

    // Grab a new instance for our purpose
    hInstance = GetModuleHandleA(NULL);

    ex.cbSize = sizeof(WNDCLASSEX);
    ex.style = 0; //CS_HREDRAW|CS_VREDRAW|CS_OWNDC;
    ex.lpfnWndProc = (WNDPROC)DirectX_10_Window::WindowProcedure;
    ex.cbClsExtra = 0;
    ex.cbWndExtra = 0;
    ex.hInstance = hInstance;
    ex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    ex.hCursor = LoadCursor(NULL, IDC_ARROW);
    ex.hbrBackground = NULL;
    ex.lpszMenuName = NULL;
    ex.lpszClassName = L"DirectX_10_";
    ex.hIconSm = NULL;

    ATOM result = RegisterClassEx(&ex);

    if( result == 0 )
        throw std::runtime_error("Could not create the window class!");
}

//=============================================================================
/**
** <summary> Function will select a proper pixel format for desired parameters </summary>
**
*/
void DirectX_10_Window::choosePixelFormat()
{
    /// TODO: Implement DX Pixel format
    if( !true )
        throw std::runtime_error( "Couldn't find a suitable pixel format for DirectX_10_ window!" );
}

//=============================================================================
/**
** <summary> This is the window message processor</summary>
**
*/
int DirectX_10_Window::WindowProcedure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        // Check if the window is being destroyed.
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }

        // Check if the window is being closed.
        case WM_CLOSE:
        {
            PostQuitMessage(0);        
            return 0;
        }

        // All other messages pass to the message handler in the system class.
        default:
        {
            if( g_ptrKernelMsgHandler )
                return g_ptrKernelMsgHandler(hWnd,message,wParam,lParam);
            else
                return DefWindowProc(hWnd, message, wParam,lParam ); //ApplicationHandle->MessageHandler(hwnd, umessage, wparam, lparam);
        }
    }
}

int DirectX_10_Window::width() const
{
    return m_windowWidth;
}

int DirectX_10_Window::height() const
{
    return m_windowHeight;
}